/*****************************************************************************/
/* Include Files */

#include "Display.h"

#include "Font24.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <linux/fb.h>
#include <linux/kd.h>

/*****************************************************************************/
/* Constant Definitions */

#ifndef DISPLAY_REAL_WIDTH
#define DISPLAY_REAL_WIDTH DISPLAY_WIDTH
#endif /*DISPLAY_REAL_WIDTH*/

#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000
#endif

/*****************************************************************************/
/* Variable Declarations */
static int DISPLAY_WIDTH = 0;//1024;
static int DISPLAY_HEIGHT = 0;//600;

static TBOOL Is_Display_Initialized = FALSE;
#ifdef SUPPORT_PWM_CTRL
static TBOOL Is_Pwm_Initialized = FALSE;
#endif /*SUPPORT_PWM_CTRL*/

static TUINT16* Default_Frame_Buffer=0;
static int frame_buffer_size=0;

static T_FONT_TYPE   Display_Font;
static const TUINT8* Display_Icon_Data;
static const TUINT8* Display_Font_Data;
static TUINT32       Display_Font_Width;
static TUINT32       Display_Font_Height;

static TUINT16 Display_Foreground_Color;
static TUINT16 Display_Background_Color;

static int                Fd_Display = -1;

#ifdef SUPPORT_PWM_CTRL
static int Fd_Pwm = -1;
#endif /*SUPPORT_PWM_CTRL*/

#define FB_PATH "/dev/graphics/fb0"


/*****************************************************************************/
/* Display */

TINT32 Display_Initialize(void)
{
	struct fb_fix_screeninfo fix_info;
	struct fb_var_screeninfo var_info;
	void *start_mem;
	
    if (! Is_Display_Initialized)
    {
        Is_Display_Initialized = TRUE;
        Fd_Display = -1;
		
		Fd_Display = open(FB_PATH,O_RDWR);
	    if (Fd_Display < 0)
	        {
				printf("Open fb0 fail");
				Display_Cleanup();
	            return STATUS_INITIALIZE_ERROR;
	        }

	    if (ioctl(Fd_Display, FBIOGET_FSCREENINFO, &fix_info) < 0) 
			{
		        printf("failed to get fb0 info");
				Display_Cleanup();
		        return STATUS_INITIALIZE_ERROR;
	    	}
	    if (ioctl(Fd_Display, FBIOGET_VSCREENINFO, &var_info) < 0)
		{
	        printf("failed to get fb0 var_info info");
	        Display_Cleanup();
			return STATUS_INITIALIZE_ERROR;
	    }
#if 0
	 	if ((var_info.xres != DISPLAY_WIDTH) || (var_info.yres != DISPLAY_HEIGHT))
        {
			printf("The width of the different");
			Display_Cleanup();
            return STATUS_DISPLAY_DIMENSION_MISMATCH;
        }
#else
		//if (var_info.xres != DISPLAY_WIDTH)
			DISPLAY_WIDTH = var_info.xres;
			
		//if (var_info.yres != DISPLAY_HEIGHT)
			DISPLAY_HEIGHT = var_info.yres;
#endif
	    start_mem = mmap(0, fix_info.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, Fd_Display, 0);
	    if (start_mem == MAP_FAILED) 
		{
	        perror("failed to mmap framebuffer");
			Display_Cleanup();
	        return -1;
	    }

        Default_Frame_Buffer = (TUINT16*) start_mem;
		frame_buffer_size = fix_info.smem_len;
		
        Display_Font = FONT_NONE;
        Display_Font_Data = 0;
        Display_Icon_Data = 0;
        Display_Font_Width = 0;
        Display_Font_Height = 0;
        Display_Foreground_Color = RGB565(255, 255,255);
        Display_Background_Color = RGB565(255,0, 0);
    }
    return STATUS_OK;
}

void Display_Cleanup(void)
{
    if (Is_Display_Initialized)
    {
		if (Default_Frame_Buffer)
			munmap((void*)Default_Frame_Buffer, frame_buffer_size);
		
        if (Fd_Display)
        {
            close(Fd_Display);
            Fd_Display = -1;
        }
        Is_Display_Initialized = FALSE;
    }
}

#ifdef SUPPORT_PRIMARY_LAYER
TINT32 Display_Set_PrimaryFrameBuffer(void* frame_buffer)
{
    if (! Is_Display_Initialized)
    {
        return STATUS_NOT_INITIALIZED;
    }

    return STATUS_OK;
}
#endif /*SUPPORT_PRIMARY_LAYER*/

TINT32 Display_Set_DefaultLayer(T_LAYER_TYPE layer)
{
    if (! Is_Display_Initialized)
    {
        return STATUS_NOT_INITIALIZED;
    }

    return STATUS_INVALID_PARAMETER;
}

TUINT16* Display_Get_CurrentFrameBuffer(void)
{
    return Default_Frame_Buffer;
}

void Display_Set_Color(TUINT16 foreground_color,
                       TUINT16 background_color)
{
    Display_Foreground_Color = foreground_color;
    Display_Background_Color = background_color;
}

void Display_Get_Color(TUINT16* foreground_color,
                       TUINT16* background_color)
{
    *foreground_color = Display_Foreground_Color;
    *background_color = Display_Background_Color;
}

TINT32 Display_Set_Font(T_FONT_TYPE type)
{
    switch (type)
    {
    case FONT_TYPE_24:
        Display_Font_Data = Font_Data_24;
        Display_Icon_Data = Icon_Data_24;
        Display_Font_Width = Font_Width_24;
        Display_Font_Height = Font_Height_24;
        break;
    default:
        return STATUS_INVALID_PARAMETER;
    }
    Display_Font = type;
    return STATUS_OK;
}

T_FONT_TYPE Display_Get_Font(void)
{
    return Display_Font;
}

TUINT32 Display_Get_FontWidth(void)
{
    return Display_Font_Width;
}

TUINT32 Display_Get_FontHeight(void)
{
    return Display_Font_Height;
}

TUINT32 Display_Get_DisplayWidth(void)
{
    return DISPLAY_WIDTH;
}

TUINT32 Display_Get_DisplayHeight(void)
{
    return DISPLAY_HEIGHT;
}

TINT32 Display_Clear(TUINT16* frame_buffer)
{
    register TUINT32* ptr;
    register TUINT32  i, j;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    for (i = 0; i < DISPLAY_HEIGHT; i++)
    {
        ptr = (TUINT32*) ((TUINT32) (frame_buffer + DISPLAY_REAL_WIDTH*i));
        for (j = 0; j < DISPLAY_WIDTH; j += 2)
        {
            *ptr++ = 0x00000000;
        }
    }
	return STATUS_OK;
}

TINT32 Display_Print_Char(TUINT32  x,
                          TUINT32  y,
                          TUINT8   data,
                          TUINT16* frame_buffer)
{
    TUINT32                byte_width;
    TUINT32                font_size;
    register const TUINT8* fptr;
    register TUINT16*      sptr;
    register TUINT32       i, j, k, l;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    if (! Display_Font_Data)
    {
        return STATUS_DISPLAY_FONT_ERROR;
    }
    if ((x > DISPLAY_WIDTH - Display_Font_Width) || (y > DISPLAY_HEIGHT - Display_Font_Height))
    {
        return STATUS_INVALID_PARAMETER;
    }
    byte_width = (Display_Font_Width + 7)/8;
    font_size = byte_width*Display_Font_Height;
    fptr = Display_Font_Data + ((data >= 0x20) && (data < 0x80) ? data - 0x20 : 0)*font_size;
    sptr = frame_buffer + DISPLAY_REAL_WIDTH*y + x;
    for (i = 0; i < Display_Font_Height; i++)
    {
        j = Display_Font_Width;
        while (1)
        {
            k = TMIN(j, 8);
            for (l = 0; l < k; l++)
            {
                *sptr++ = ((((*fptr) >> (7 - l))&0x01) != 0x00 ? Display_Foreground_Color : Display_Background_Color);
            }
            fptr++;
            if (j <= 8)
            {
                break;
            }
            j -= 8;
        }
        sptr += (DISPLAY_REAL_WIDTH - Display_Font_Width);
    }
    return STATUS_OK;
}

TINT32 Display_Print_String(TUINT32       x,
                            TUINT32       y,
                            const TUINT8* data,
                            TUINT16*      frame_buffer)
{
    TUINT32 char_x;
    TUINT32 i;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    if (! Display_Font_Data)
    {
        return STATUS_DISPLAY_FONT_ERROR;
    }
    if (y > DISPLAY_HEIGHT - Display_Font_Height)
    {
        return STATUS_INVALID_PARAMETER;
    }
    for (char_x = x, i = 0; data[i] != '\0'; char_x += Display_Font_Width, i++)
    {
        Display_Print_Char(char_x, y, data[i], frame_buffer);
    }
    return STATUS_OK;
}

TINT32 Display_Print_DecNumber(TUINT32  x,
                               TUINT32  y,
                               TUINT32  length,
                               TUINT32  value,
                               TUINT16* frame_buffer)
{
    TUINT8  buffer[11];
    TUINT32 i;

    for (i = (length < 10 ? length : 10); i > 0; i--)
    {
        buffer[i - 1] = ((TUINT8) (value%10)) + '0';
        value /= 10;
    }
    buffer[length < 10 ? length : 10] = 0x00;
    return Display_Print_String(x, y, buffer, frame_buffer);
}

TINT32 Display_Print_HexNumber(TUINT32  x,
                               TUINT32  y,
                               TUINT32  length,
                               TUINT32  value,
                               TUINT16* frame_buffer)
{
    TUINT8  buffer[9];
    TUINT8  digit;
    TUINT32 i;

    for (i = (length < 8 ? length : 8); i > 0; i--)
    {
        digit = (TUINT8) (value%16);
        if (digit < 10)
        {
            buffer[i - 1] = digit + '0';
        }
        else
        {
            buffer[i - 1] = digit - 10 + 'A';
        }
        value /= 16;
    }
    buffer[length < 8 ? length : 8] = 0x00;
    return Display_Print_String(x, y, buffer, frame_buffer);
}

TINT32 Display_Print_Icon(TUINT32   x,
                          TUINT32   y,
                          T_ICON_ID icon_id,
                          TUINT16*  frame_buffer)
{
    TUINT32                byte_width;
    TUINT32                font_size;
    register const TUINT8* fptr;
    register TUINT16*      sptr;
    register TUINT32       i, j, k, l;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    if (! Display_Icon_Data)
    {
        return STATUS_DISPLAY_FONT_ERROR;
    }
    if ((x > DISPLAY_WIDTH - Display_Font_Width) || (y > DISPLAY_HEIGHT - Display_Font_Height))
    {
        return STATUS_INVALID_PARAMETER;
    }
    byte_width = (Display_Font_Width + 7)/8;
    font_size = byte_width*Display_Font_Height;
    fptr = Display_Icon_Data + icon_id*font_size;
    sptr = frame_buffer + DISPLAY_REAL_WIDTH*y + x;
    for (i = 0; i < Display_Font_Height; i++)
    {
        j = Display_Font_Width;
        while (1)
        {
            k = TMIN(j, 8);
            for (l = 0; l < k; l++)
            {
                *sptr++ = ((((*fptr) >> (7 - l))&0x01) != 0x00 ? Display_Foreground_Color : Display_Background_Color);
            }
            fptr++;
            if (j <= 8)
            {
                break;
            }
            j -= 8;
        }
        sptr += (DISPLAY_REAL_WIDTH - Display_Font_Width);
    }
    return STATUS_OK;
}

TINT32 Display_Draw_Pixel(TUINT32  x,
                          TUINT32  y,
                          TUINT16  color,
                          TUINT16* frame_buffer)
{
    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    frame_buffer[DISPLAY_REAL_WIDTH*y + x] = color;
    return STATUS_OK;
}

TINT32 Display_Draw_Line(TUINT32  x1,
                         TUINT32  y1,
                         TUINT32  x2,
                         TUINT32  y2,
                         TUINT16  color,
                         TUINT16* frame_buffer)
{
    TUINT32           value;
    register TUINT16* ptr;
    register TUINT32  i;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    if ((x1 >= DISPLAY_WIDTH) || (x2 >= DISPLAY_WIDTH) || (y1 >= DISPLAY_HEIGHT) || (y2 >= DISPLAY_HEIGHT))
    {
        return STATUS_INVALID_PARAMETER;
    }
    if (x1 > x2)
    {
        value = x1;
        x1 = x2;
        x2 = value;
    }
    if (y1 > y2)
    {
        value = y1;
        y1 = y2;
        y2 = value;
    }
    if (x1 == x2)
    {
        ptr = frame_buffer + DISPLAY_REAL_WIDTH*y1 + x1;
        for (i = y1; i <= y2; i++)
        {
            *ptr = color;
            ptr += DISPLAY_REAL_WIDTH;
        }
        return STATUS_OK;
    }
    if (y1 == y2)
    {
        ptr = frame_buffer + DISPLAY_REAL_WIDTH*y1 + x1;
        for (i = x1; i <= x2; i++)
        {
            *ptr++ = color;
        }
        return STATUS_OK;
    }
    return STATUS_OK;
}

TINT32 Display_Draw_Rectangle(TUINT32  x1,
                              TUINT32  y1,
                              TUINT32  x2,
                              TUINT32  y2,
                              TUINT16  color,
                              TUINT16* frame_buffer)
{
    TINT32 status;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    status = Display_Draw_Line(x1, y1, x2, y1, color, frame_buffer);
    if (status != STATUS_OK)
    {
        return status;
    }
    status = Display_Draw_Line(x1, y2, x2, y2, color, frame_buffer);
    if (status != STATUS_OK)
    {
        return status;
    }
    status = Display_Draw_Line(x1, y1, x1, y2, color, frame_buffer);
    if (status != STATUS_OK)
    {
        return status;
    }
    status = Display_Draw_Line(x2, y1, x2, y2, color, frame_buffer);
    if (status != STATUS_OK)
    {
        return status;
    }
    return STATUS_OK;
}

TINT32 Display_Fill_Block(TUINT32  x,
                          TUINT32  y,
                          TUINT32  width,
                          TUINT32  height,
                          TUINT16  color,
                          TUINT16* frame_buffer)
{
    register TUINT16* ptr;
    register TUINT32  i, j;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    if ((x >= DISPLAY_WIDTH) || (y >= DISPLAY_HEIGHT))
    {
        return STATUS_INVALID_PARAMETER;
    }
    width = TMIN(width, DISPLAY_WIDTH - x);
    height = TMIN(height, DISPLAY_HEIGHT - y);
    ptr = frame_buffer + y*DISPLAY_REAL_WIDTH + x;
    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            *ptr++ = color;
        }
        ptr += (DISPLAY_REAL_WIDTH - width);
    }
    return STATUS_OK;
}

TINT32 Display_BitBlt(TUINT32        x,
                      TUINT32        y,
                      TUINT32        width,
                      TUINT32        height,
                      const TUINT16* data,
                      TUINT16*       frame_buffer)
{
    register const TUINT16* sptr;
    register TUINT16*       dptr;
    register TUINT32        i, j;

    if (! frame_buffer)
    {
        if (! Default_Frame_Buffer)
        {
            return STATUS_DISPLAY_FRAME_BUFFER_ERROR;
        }
        frame_buffer = Default_Frame_Buffer;
    }
    if ((x + width > DISPLAY_WIDTH) || (y + height > DISPLAY_HEIGHT) || (! data))
    {
        return STATUS_INVALID_PARAMETER;
    }
    for (sptr = data, dptr = frame_buffer + y*DISPLAY_REAL_WIDTH + x, i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            *dptr++ = *sptr++;
        }
        dptr += (DISPLAY_REAL_WIDTH - width);
    }
    return STATUS_OK;
}

/*****************************************************************************/
/* End of File */
